package service

import (
	"fmt"
	"github.com/didi/nightingale/src/common/notify"
	"github.com/didi/nightingale/src/models"
	"github.com/toolkits/pkg/logger"
	"github.com/toolkits/pkg/slice"
	"github.com/toolkits/pkg/str"
	"strconv"
	"strings"
	"unsafe"
)

func ScheduleTask(id int64) {
	logger.Debugf("task[%d] scheduling...", id)

	count, err := models.WaitingHostCount(id)
	if err != nil {
		logger.Errorf("cannot get task[%d] waiting host count: %v", id, err)
		return
	}

	if count == 0 {
		cleanDoneTask(id)
		return
	}

	action, err := models.TaskActionGet("id=?", id)
	if err != nil {
		logger.Errorf("cannot get task[%d] action: %v", id, err)
		return
	}

	if action == nil {
		logger.Errorf("[W] no action found of task[%d]", id)
		return
	}

	switch action.Action {
	case "start":
		startTask(id, action)
	case "pause":
		return
	case "cancel":
		return
	case "kill":
		return
	default:
		logger.Errorf("unknown action: %s of task[%d]", action.Action, id)
	}
}

func cleanDoneTask(id int64) {
	ingCount, err := models.IngStatusHostCount(id)
	if err != nil {
		logger.Errorf("cannot get task[%d] ing status host count: %v", id, err)
		return
	}

	if ingCount > 0 {
		return
	}

	//add by hanbq执行后置任务和发推推
	meta, err := models.TaskMetaGetByID(id)
	if err != nil {
		logger.Errorf("cannot get task[%d] meta: %v", id, err)
	}

	if meta != nil {
		hosts, err := meta.Hosts()
		if err != nil {
			logger.Errorf("cannot get host status, task[%d] meta: %v", id, err)
		}
		var succNum int
		for _, v := range hosts {
			if v.Status == "success" {
				succNum++
			}
		}
		succRatio := float32(succNum) / float32(len(hosts))
		if err = meta.UpdateSuccRation(succRatio); err != nil {
			logger.Errorf("cannot update succration task[%d] meta: %v", id, err)
		}

		if meta.Groups != "" {
			groups := strings.Split(meta.Groups, ",")
			if len(groups) != 0 {
				reply := fmt.Sprintf("Fly任务执行中心 执行结束通知\n任务名称:%s\n任务执行状态:%.2f%%\n任务查看地址:https://fly.so.qihoo.net/job/tasks/%d/result\n",
					meta.Title, succRatio, meta.Id)
				if meta.Prev != 0 {
					reply = reply + fmt.Sprintf("前置任务:https://fly.so.qihoo.net/job/tasks/%d/result\n", meta.Prev)
				}
				//这对下一个任务
				if meta.Next != "" {
					logger.Debugf("task[%d] next is: %s", id, meta.Next)
					nextMap := map[string]int64{}
					next := strings.Trim(meta.Next, "\n")
					next = strings.Replace(next, "，", ",", -1)
					next = strings.ToLower(next)
					nextGroup := strings.Split(next, ",")

					for _, v := range nextGroup {
						//v = strings.TrimSpace(v)
						nexts := strings.Split(v, "=")
						logger.Debugf("task[%d] nexts is: %+v", id, nexts)
						if len(nexts) == 2 {
							if tmpid, err := strconv.ParseInt(strings.TrimSpace(nexts[1]), 10, 64); err == nil {
								if strings.Contains(nexts[0], "succ") {
									nextMap["succ"] = tmpid
								} else if strings.Contains(nexts[0], "fail") {
									nextMap["fail"] = tmpid
								} else if strings.Contains(nexts[0], "all") {
									nextMap["all"] = tmpid
								}
							}
						} else if len(nexts) == 1 {
							if tmpid, err := strconv.ParseInt(strings.TrimSpace(nexts[0]), 10, 64); err == nil {
								nextMap["all"] = tmpid
							}
						}
					}
					logger.Debugf("task[%d] nextMap is: %+v, succration:%f", id, nextMap, succRatio)
					if succRatio < 1 {
						if id, ok := nextMap["fail"]; ok {
							reply = reply + fmt.Sprintf("失败后置任务模板:https://fly.so.qihoo.net/job/tpls/%d/detail\n", id)
							go TriggerNext(id, meta.Id, meta.Creator, meta.Span)
						}
					} else {
						if id, ok := nextMap["succ"]; ok {
							reply = reply + fmt.Sprintf("成功后置任务模板:https://fly.so.qihoo.net/job/tpls/%d/detail\n", id)
							go TriggerNext(id, meta.Id, meta.Creator, meta.Span)
						}
					}
					if id, ok := nextMap["all"]; ok {
						reply = reply + fmt.Sprintf("后置任务模板:https://fly.so.qihoo.net/job/tpls/%d/detail\n", id)
						logger.Debugf("next task[%d] metaid:%d ", id, meta.Id)
						go TriggerNext(id, meta.Id, meta.Creator, meta.Span)
					}
				}
				go notify.Send(groups, reply, "", "im")
			}
		}
	}

	err = models.CleanDoneTask(id)
	if err != nil {
		logger.Errorf("cannot clean done task[%d]: %v", id, err)
	}

	logger.Debugf("task[%d] done", id)
}

func startTask(id int64, action *models.TaskAction) {
	meta, err := models.TaskMetaGetByID(id)
	if err != nil {
		logger.Errorf("cannot get task[%d] meta: %v", id, err)
		return
	}

	if meta == nil {
		logger.Errorf("task[%d] meta lost", id)
		return
	}

	count, err := models.UnexpectedHostCount(id)
	if err != nil {
		logger.Errorf("cannot get task[%d] unexpected host count: %v", id, err)
		return
	}

	if count > int64(meta.Tolerance) {
		err = action.Update("pause")
		if err != nil {
			logger.Errorf("cannot update task[%d] action to 'pause': %v", id, err)
		}
		return
	}

	waitings, err := models.WaitingHostList(id)
	if err != nil {
		logger.Errorf("cannot get task[%d] waiting host: %v", id, err)
		return
	}

	waitingsCount := len(waitings)
	if waitingsCount == 0 {
		return
	}

	doingsCount, err := models.DoingHostCount("id=?", id)
	if err != nil {
		logger.Errorf("cannot get task[%d] doing host count: %v", id, err)
		return
	}

	need := meta.Batch - int(doingsCount)
	//update by hanbq:分布式调度任务，batch=-1全并发
	if meta.Batch == 0 || meta.Batch == -1 {
		need = waitingsCount
	}

	if need <= 0 {
		return
	}

	if need > waitingsCount {
		need = waitingsCount
	}

	arr := str.ParseCommaTrim(meta.Pause)
	end := need
	for i := 0; i < need; i++ {
		if slice.ContainsString(arr, waitings[i].Host) {
			end = i + 1
			err = action.Update("pause")
			if err != nil {
				logger.Errorf("cannot update task[%d] action to 'pause': %v", id, err)
				return
			}
			break
		}
	}

	err = models.RunWaitingHosts(waitings[:end])
	if err != nil {
		logger.Errorf("cannot run waiting hosts: %v", err)
		return
	}
}

func TriggerNext(tplid, trigerby int64, userName string, spanid int64) (taskid int64, err error) {
	obj, err := models.TaskTplGet("id=?", tplid)
	if err != nil {
		logger.Errorf("cannot find this tasktpl:%d, triger by prev:%d, %v", tplid, trigerby, err)
		return
	}
	if obj == nil {
		logger.Errorf("cannot find next job:%d, triger by prev:%d", tplid, trigerby)
		return
	}
	hosts, err := obj.Hosts()
	if err != nil {
		logger.Errorf("cannot find this tasktpl:%d hosts, triger by prev:%d, %v", tplid, trigerby, err)
		return
	}

	if len(hosts) == 0 {
		logger.Errorf("cannot trigger next job:%d, triger by prev:%d, no hosts in tpl", tplid, trigerby)
		return
	}
	if userName == "" {
		userName = obj.Creator
	}

	var trigger int = 0
	if userName == "trigger" && spanid != -1 {
		trigger = *(*int)(unsafe.Pointer(&spanid))
	}

	if spanid == -1 {
		spanid = 0
	}

	task := &models.TaskMeta{
		Title:     obj.Title,
		Account:   obj.Account,
		Batch:     obj.Batch,
		Tolerance: obj.Tolerance,
		Timeout:   obj.Timeout,
		Pause:     obj.Pause,
		Script:    obj.Script,
		Args:      obj.Args,
		Groups:    obj.Groups, //执行结束告知
		Prev:      trigerby,
		Span:      spanid,
		Trigger:   trigger,
		Next:      obj.Next,
		Node:      obj.Node,
		NodeId:    obj.NodeId,
		Tplid:     tplid,
		Creator:   userName,
	}

	defer func() {
		if task.Groups != "" {
			groups := strings.Split(task.Groups, ",")
			if len(groups) != 0 {
				var reply string
				if err != nil {
					reply = fmt.Sprintf("Fly任务执行中心 任务触发通知\n任务名称:%s\n触发失败请联系管理员\n", task.Title)
				} else {
					reply = fmt.Sprintf("Fly任务执行中心 任务触发通知\n任务名称:%s\n任务查看地址:https://fly.so.qihoo.net/job/tasks/%d/result\n",
						task.Title, task.Id)
				}

				if task.Prev != 0 {
					reply = reply + fmt.Sprintf("前置任务:https://fly.so.qihoo.net/job/tasks/%d/result\n\n", task.Prev)
				}
				go notify.Send(groups, reply, "", "im")
			}
		}
	}()

	err = task.Save(hosts, "start")
	if err != nil {
		logger.Errorf("save task meta failed, triger by prev:%d, %v", trigerby, err)
	}
	taskid = task.Id

	if task.Next != "" && task.Id != 0 && spanid == 0 {
		err2 := task.UpdateSpan(task.Id)
		if err2 != nil {
			logger.Errorf("task update span failed, taskid:%d, %v", task.Id, err2)
		}
	}

	return
}
